import { join } from "node:path";
import is from "is-type-of";
import { inspect } from "util";
import assert from "assert";
import debugFactory from "debug";
const debug = debugFactory("core:loader/mixin/middleware");
import { addDirectory } from "../../utils/index.js";
/**
 * @function loadMiddleware
 * app.config.middlewares 为中间件的配置，
 * 格式为：['string', {name: 'string', enable: true, ignore: true, match: ctx => true, options:{}}]
 * @function Loader#loadMiddleware
 * @param {Object} options - LoaderOptions
 * @example
 * ```js
 * // src/middleware/status.js
 * module.exports = function(options) {
 *   // options == app.config.middlewares
 *   return function(ctx, next) {
 *     return next();
 *   }
 * }
 * ```
 */
export default async function loadMiddleware(ctx) {
  ctx.timing.time("加载middleware时间");
  const app = ctx.app;
  const directory = [join(ctx.root, "src/middleware")];
  directory.push(...addDirectory(join(ctx.root, "src/modules"), 'middleware'));
  // 加载中间件到 app.middlewares
  const options = {
    iscall: false,
    isoverride: true,
    caseStyle: "lower",
    directory,
    isflat: true,
  };
  await ctx.loadToApp(directory, "middlewares", options);
  for (const name in app.middlewares) {
    Object.defineProperty(app.middleware, name, {
      get() {
        return app.middlewares[name];
      },
      enumerable: false,
      configurable: false,
    });
  }
  const middlewares = ctx.config.middlewares;
  debug("组合middlewareNames: %j", middlewares);
  const middlewareMap = new Map();
  for (const item of middlewares) {
    let name,
      opt = {};
    if (!item) {
      continue;
    } else if (is.string(item)) {
      name = item;
    } else {
      name = item.name;
      if (!name) {
        throw new TypeError(`必须设置Middleware的name属性`);
      }
      opt = item;
    }
    if (!app.middleware[name]) {
      throw new TypeError(`Middleware ${name} not found`);
    }
    if (middlewareMap.has(name)) {
      throw new TypeError(`Middleware ${name} redefined`);
    }
    middlewareMap.set(name, true);
    let middlewareFn = app.middleware[name];

    middlewareFn = middlewareFn(app, opt.options || {});
    assert(is.function(middlewareFn), `中间件${name}必须是一个function, 现在是${inspect(middlewareFn)}`);


    // 处理要传入中间件的配置项
    middlewareFn._name = name;
    middlewareFn = wrapMiddleware(middlewareFn, opt);

    if (middlewareFn) {
      assert(
        is.function(middlewareFn),
        `中间件${name}必须是一个function, 现在是${inspect(middlewareFn)}`
      );
      if (debug.enabled) {
        middlewareFn = debugMiddlewareWrapper(middlewareFn);
      }
      app.use(middlewareFn);
      debug("使用中间件: %s 和配置: %j", name, opt);
    } else {
      debug("禁用中间件: %s", name);
    }
  }
  ctx.timing.timeEnd("加载middleware时间");
}

/**
 * @function wrapMiddleware
 * @description middlewares support config.enable, config.ignore and config.match
 * @param {Function} mw 中间件
 * @param {Object} config
 */
function wrapMiddleware(mw, config) {
  // support config.enable
  if (config.enable === false) {
    return;
  }

  // support config.match and config.ignore
  if (!config.match && !config.ignore) {
    return mw;
  }

  const fn = (ctx, next) => {
    if (!config.match(ctx)) {
      return next();
    }
    return mw(ctx, next);
  };
  fn._name = `${mw._name}middlewareWrapper`;
  return fn;
}

function debugMiddlewareWrapper(mw) {
  const fn = (ctx, next) => {
    debug("[%s %s] enter middleware: %s", ctx.method, ctx.url, mw._name);
    return mw(ctx, next);
  };
  fn._name = mw._name + "DebugWrapper";
  return fn;
}
